﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using ToracTechnologies.ExpressionTreeLibrary.DynamicApi.Framework;

namespace ToracTechnologies.ExpressionTreeLibrary.DynamicApi
{

    /// <summary>
    /// Class is used for dynamic linq helpers (building expression tree's, etc)
    /// </summary>
    public static class LinqUtilities
    {

        #region Paginate

        /// <summary>
        /// Takes IQueryable Of Objects And Grabs The Current Page We Are On
        /// </summary>
        /// <typeparam name="T">Type Of Records That Are Returned</typeparam>
        /// <param name="QueryToModify">Query To Modify</param>
        /// <param name="CurrentPageNumber">What Page Number Are You Currently On</param>
        /// <param name="HowManyRecordsPerPage">How Many Records Per Page</param>
        /// <returns>IQueryable Of T</returns>
        public static IQueryable<T> PaginateResults<T>(IQueryable<T> QueryToModify, int CurrentPageNumber, int HowManyRecordsPerPage)
        {
            //use the entity framework helper method
            return EntityFrameworkUtilities.PaginateResults<T>(QueryToModify, CurrentPageNumber, HowManyRecordsPerPage);
        }

        /// <summary>
        /// Takes A List Of Objects And Grabs The Current Page We Are On
        /// </summary>
        /// <typeparam name="T">Type Of Records That Are Returned</typeparam>
        /// <param name="CollectionToPaginate">Query To Modify</param>
        /// <param name="CurrentPageNumber">What Page Number Are You Currently On</param>
        /// <param name="HowManyRecordsPerPage">How Many Records Per Page</param>
        /// <returns>IQueryable Of T</returns>
        public static IQueryable<T> PaginateResults<T>(IEnumerable<T> CollectionToPaginate, int CurrentPageNumber, int HowManyRecordsPerPage)
        {
            //use the entity framework helper method
            return EntityFrameworkUtilities.PaginateResults(CollectionToPaginate.AsQueryable(), CurrentPageNumber, HowManyRecordsPerPage);
        }

        #endregion

        #region Order By

        /// <summary>
        /// Used to dynamically create the order by statement. Used if you need to get a func
        /// </summary>
        /// <typeparam name="T">Type of the object passed in</typeparam>
        /// <param name="OrderByAscending">Order Ascending Or Descending</param>
        /// <param name="QueryToModify">Query To Modify</param>
        /// <param name="PropertyNameToCheck">Property Name To Check</param>
        /// <returns>IOrderedQueryable</returns>
        public static IOrderedQueryable<T> OrderByLinqStatement<T>(IQueryable<T> QueryToModify, string PropertyNameToCheck, bool OrderByAscending)
        {
            //this is faster since you don't cast this to an object. However It doesn't return a func incase you need to do something with it

            // there is an extension method off IQueryable...so you can do collection.OrderBy("Id");
            //ToracTechnologies.Library.ExtensionMethods.IQueryable.IQueryableExtMethods
            return EntityFrameworkUtilities.OrderBy(QueryToModify, PropertyNameToCheck, OrderByAscending);
        }

        /// <summary>
        /// Used to dynamically create the order by statement. Used if you need to get a func
        /// </summary>
        /// <typeparam name="T">Type of the object passed in</typeparam>
        /// <param name="PropertyNameToCheck">Property Name To Check</param>
        /// <returns>Compiled Lamda</returns>
        public static Func<T, object> OrderByLinqStatement<T>(string PropertyNameToCheck)
        {
            //this is slower than the Order By Where you pass back IOrderedQueryable (because of the object type in the func)
            //this is more flexible since you get a func back. It's not that much faster, but incase you need optimal performance

            //example on how to call this
            //var sql = LinqUtilities.OrderBy<TestTable>("Description");
            //var z = DP.Fetch<TestTable>(false).OrderByDescending(sql);

            //works only with Linq To Objects...See EntityFrameworkUtilities...for OrderBy For EF

            //grab the Member Expression
            ParameterBuilderResults PropertyExpression = ParameterBuilder.BuildParameterFromStringName<T>(PropertyNameToCheck);

            //convert the value to an object so we can use an int and cast it to an object and have the compiler figure out the type
            Expression ConvertedExpression = Expression.Convert(PropertyExpression.PropertyMemberExpression, typeof(object));

            //combine the lamda and return it
            return Expression.Lambda<Func<T, object>>(ConvertedExpression, PropertyExpression.ParametersForExpression).Compile();
        }

        #endregion

        #region String Contains

        /// <summary>
        /// Builds a dynamic expression which test if the property name contains the ContainTest string Passed in
        /// </summary>
        /// <typeparam name="T">Type Of The Object Passed In (Object Container Of PropertyNameToCheck)</typeparam>
        /// <param name="PropertyNameToCheck">Property Name To Check</param>
        /// <param name="ContainsTest">What To Check For In The Property</param>
        /// <param name="CaseSensitive">Make it case sensitive on the search</param>
        /// <returns>Linq Expression</returns>
        public static Expression<Func<T, bool>> StringContains<T>(string PropertyNameToCheck, string ContainsTest, bool CaseSensitive)
        {
            return ExpressionBuilder.StringContains<T>(PropertyNameToCheck, ContainsTest, CaseSensitive, true);
        }

        #endregion

    }

}
